package com.qianli.cashmama.trade.domain.factory.impl;

import com.alibaba.fastjson.JSON;
import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.common.utils.DateUtil;
import com.fqgj.common.utils.DecimalUtils;
import com.fqgj.common.utils.TimeUtils;
import com.fqgj.exception.common.ApplicationException;
import com.qianli.cashmama.trade.client.bill.request.BillPayRequest;
import com.qianli.cashmama.trade.common.enums.BillPaidStatusEnum;
import com.qianli.cashmama.trade.common.enums.BillStatusEnum;
import com.qianli.cashmama.trade.common.enums.TradeBorrowDurationEnum;
import com.qianli.cashmama.trade.common.error.TradeErrorCodeEnum;
import com.qianli.cashmama.trade.common.exceptions.TradeException;
import com.qianli.cashmama.trade.common.utils.CalculatorUtils;
import com.qianli.cashmama.trade.domain.factory.BillBaseDomainFactory;
import com.qianli.cashmama.trade.domain.model.*;
import com.robert.vesta.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: sm
 * Date: 2017/7/24
 * Time: 上午10:51
 */
@Service
public class BillBaseDomainFactoryImpl implements BillBaseDomainFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(BillBaseDomainFactoryImpl.class);

    /**
     * 创建账单
     *
     * @param tradeBase
     * @param receivedDate
     * @return
     */
    @Override
    public List<BillBase> composeBillBase(TradeBase tradeBase, Date receivedDate) {
        try {
            int totalPeriods = tradeBase.getTotalPeriod();
            int periodLength = tradeBase.getPeriodLength();
            BigDecimal borrowCapital = CalculatorUtils.calculateBorrowCapital(tradeBase.getBorrowTotalCapital(), totalPeriods);
            BigDecimal interest = CalculatorUtils.calculateInterestFee(borrowCapital, tradeBase.getBorrowRatio(), tradeBase.getPeriodLength());
            BigDecimal receivedCapital = CalculatorUtils.calculateReceivedCapital(tradeBase.getReceivedTotalCapital(), totalPeriods);


            List<BillBase> billList = new ArrayList<>(tradeBase.getTotalPeriod());
            //处理时间
            //Date repaymentDate = new Date();
            Date repaymentDate = receivedDate;
            for (int i = 1; i <= totalPeriods; i++) {
                BillBase billBase = new BillBase()
                        .setBillNo(createBillNo(tradeBase.getUserCode()))
                        .setTradeNo(tradeBase.getTradeNo())
                        .setCurrentPeriod(i)
                        .setStatus(BillStatusEnum.INIT)
                        .setPaidStatus(BillPaidStatusEnum.INIT)
                        .setPaidCapital(BigDecimal.ZERO)
                        .setDiscountCapital(BigDecimal.ZERO)
                        .setOverdueFee(BigDecimal.ZERO)
                        .setOverdueServiceFee(tradeBase.getOverdueServiceFee())
                        .setTheoreticalTotalAmount(BigDecimal.ZERO)
                        .setProductCategory(tradeBase.getProductCategory())
                        .setProductCode(tradeBase.getProductCode());

                if (TradeBorrowDurationEnum.DAY.getType() == tradeBase.getBorrowDuration()) {
                    repaymentDate = timeAppointed(repaymentDate, periodLength - 1);
                } else {
                    throw new ApplicationException("日期类型不支持");
                }

                billBase.setRepaymentDate(TimeUtils.getDate("yyyy-MM-dd", repaymentDate));
                //最后一期
                if (i == totalPeriods) {
                    billBase.setBorrowCapital(CalculatorUtils.calculateLastBorrowCapital(tradeBase.getBorrowTotalCapital(), totalPeriods))
                            .setReceivedCapital(CalculatorUtils.calculateLastReceivedCapital(tradeBase.getReceivedTotalCapital(), totalPeriods))
                            .setInterestFee(interest)
                            .setBorrowServiceFee(tradeBase.getTotalServiceFee())
                            .setRepaymentCapital(CalculatorUtils.createBillRepaymentCapital(billBase.getBorrowCapital(), interest));
                } else {
                    billBase.setBorrowCapital(borrowCapital)
                            .setReceivedCapital(receivedCapital)
                            .setInterestFee(interest)
                            .setRepaymentCapital(CalculatorUtils.createBillRepaymentCapital(borrowCapital, interest));
                }
                billList.add(billBase);
            }
            return billList;
        } catch (Exception e) {
            LOGGER.error("==组建账单列表失败==,tradeNo:{},e", tradeBase.getTradeNo(), e);
            throw new TradeException(TradeErrorCodeEnum.bill_list_compose_failed);
        }
    }

    /**
     * 更新账单信息
     *
     * @param
     * @param billPayRequest
     */
    @Override
    public void composeBillBaseForRepayment(BillBase billBase, TradeBase tradeBase, BillPayRequest billPayRequest, BigDecimal discountCapital) {
        try {
            LOGGER.info("更新账单信息,tradeNo:{},billNo:{},capital:{},discountCapital:{}", billBase.getTradeNo(), billBase.getBillNo(), billPayRequest.getCapital(), discountCapital);
            boolean isOverdue = CalculatorUtils.isOverdue(billBase.getRepaymentDate(), billPayRequest.getPayDate());
            BigDecimal overdueServiceFee = BigDecimal.ZERO;
            BigDecimal overdueFee = BigDecimal.ZERO;
            if (isOverdue) {
                int overdueDays = CalculatorUtils.overDueDays(billBase.getRepaymentDate(), billPayRequest.getPayDate());
                overdueServiceFee = tradeBase.getOverdueServiceFee();
                overdueFee = CalculatorUtils.calculateOverdueFee(billBase.getBorrowCapital(), tradeBase.getOverdueRatio(), overdueDays);
            }

            billBase.setOverdueFee(overdueFee);
            billBase.setOverdueServiceFee(overdueServiceFee);

            BigDecimal repaymentTotalCapital = CalculatorUtils.repaymentTotalCapital(billBase.getRepaymentCapital(), overdueFee, overdueServiceFee).setScale(2, BigDecimal.ROUND_HALF_UP);

            boolean payClear = CalculatorUtils.payClear(billBase.getRepaymentCapital(),
                    overdueFee,
                    overdueServiceFee,
                    billBase.getPaidCapital(),
                    new BigDecimal(billPayRequest.getCapital()),
                    discountCapital);

            billBase.setPaidCapital(billBase.getPaidCapital().add(new BigDecimal(billPayRequest.getCapital())).add(discountCapital));
            if (payClear) {
                billBase.setStatus(BillStatusEnum.PAID);
                billBase.setPaidStatus(BillPaidStatusEnum.CLEAR_REPAYMENT);
                billBase.setPayOffDate(billPayRequest.getPayDate());
            } else {
                billBase.setPaidStatus(BillPaidStatusEnum.PARTIAL_REPAYMENT);
            }
            billBase.setDiscountCapital(billBase.getDiscountCapital().add(discountCapital));
            billBase.setTheoreticalTotalAmount(repaymentTotalCapital);

            if (billBase.getPaidCapital().setScale(2, BigDecimal.ROUND_HALF_UP).compareTo(repaymentTotalCapital) > 0) {
                LOGGER.warn("更新账单信息累计还款大于应还金额,tradeNo:{},billNo:{},paidCapital:{},repaymentTotalCapital:{},capital:{},discountCapital:{}",
                        billBase.getTradeNo(), billBase.getBillNo(), billBase.getPaidCapital(), repaymentTotalCapital, billPayRequest.getCapital(), discountCapital);
            }

        } catch (Exception e) {
            LOGGER.error("还款账单组建失败,billNo:{}", billBase.getBillNo(), e);
            throw new TradeException(TradeErrorCodeEnum.bill_compose_failed);
        }
    }

    @Override
    public TradeBillDetail composeBillDetail(BillBase billBase, BillPayRequest billPayRequest, String userCode) {
        String bizNo = createBillRepaymentNo(userCode);
        BigDecimal paidCapital = new BigDecimal(billPayRequest.getCapital());
        TradeBillDetail tradeBillDetail = new TradeBillDetail()
                .setAccountedTime(billPayRequest.getPayDate())
                .setBizNo(bizNo)
                .setBankNo(billPayRequest.getBankNo())
                .setPaidCapital(paidCapital)
                .setDiscountCapital(billPayRequest.getDiscountCapital())
                .setPayBizNo(billPayRequest.getPayBizNo())
                .setPaidChannel(billPayRequest.getPaidChannel())
                .setChannelServiceFee(billPayRequest.getPaidChannelServiceFee())
                .setOperator(billPayRequest.getOperator());
        if (billBase != null) {
            tradeBillDetail.setTradeNo(billBase.getTradeNo())
                    .setBillNo(billBase.getBillNo())
                    .setProductCategory(billBase.getProductCategory())
                    .setProductCode(billBase.getProductCode());
        }
        LOGGER.info("生成当次还款领域对象TradeBillDetail-payBizNo[{}],bizNo[{}],billPayRequest[{}],tradeBillDetail[{}]"
                , billPayRequest.getPayBizNo(), bizNo, JSON.toJSONString(billPayRequest), JSON.toJSONString(tradeBillDetail));
        return tradeBillDetail;
    }

    /**
     * 做还款记账会计分录
     *
     * @param tradeBillDetail
     * @param billBase
     * @param billPayRequest
     * @param discountCapital
     * @return
     */
    @Override
    public TradeRepaymentAccounting composeBillRepaymentAccounting(TradeBillDetail tradeBillDetail, BillBase billBase, BillPayRequest billPayRequest, BigDecimal discountCapital) {
        TradeRepaymentAccounting tradeRepaymentAccounting = new TradeRepaymentAccounting()
                .setRepaymentBizNo(tradeBillDetail.getBizNo())
                .setTradeNo(billBase.getTradeNo())
                .setAccountedTime(billPayRequest.getPayDate())
                .setBillNo(billBase.getBillNo())
                .setDiscountCapital(discountCapital)
                //当次还款总金额
                .setPaidTotalCapital(tradeBillDetail.getPaidCapital())
                //当笔账单记账金额
                .setPaidCapital(billBase.getPaidCapital())
                .setRealIncome(billBase.getBorrowCapital())
                .setRealInterest(billBase.getInterestFee())
                .setRealServiceFee(billBase.getBorrowServiceFee())
                .setRealOverdueFee(billBase.getOverdueFee())
                .setRealOverdueServiceFee(billBase.getOverdueServiceFee())
                .setUserCode(billPayRequest.getUserCode())
                .setPayBizNo(billPayRequest.getPayBizNo())
                .setProductCategory(billBase.getProductCategory())
                .setProductCode(billBase.getProductCode());
        LOGGER.info("生成当次还账单记账领域对象TradeRepaymentAccounting-payBizNo[{}],billPayRequest[{}],tradeBillDetail[{}]," +
                        "billBase[{}], discountCapital[{}], tradeRepaymentAccounting[{}]"
                , billPayRequest.getPayBizNo(), tradeBillDetail, billPayRequest, discountCapital, tradeRepaymentAccounting);
        return tradeRepaymentAccounting;
    }


    /**
     * 做还款记账会计分录
     *
     * @param billAccountingList
     * @param billBase
     * @param tradeBase
     * @param billPayRequest
     * @return
     */
    @Override
    public BillAccounting composeBillAccounting(List<BillAccounting> billAccountingList, BillBase billBase, TradeBase tradeBase, BillPayRequest billPayRequest, TradeBillDetail tradeBillDetail) {
        LOGGER.info("会计记账,billNo:{},capital:{}", billBase.getBillNo(), billPayRequest.getCapital());
        BigDecimal capital = new BigDecimal(billPayRequest.getCapital());

        BigDecimal borrowCapital = billBase.getBorrowCapital();
        BigDecimal interestFee = billBase.getInterestFee();
        BigDecimal overdueServiceFee = BigDecimal.ZERO;
        BigDecimal overdueFee = BigDecimal.ZERO;
        if (CalculatorUtils.isOverdue(billBase.getRepaymentDate(), billPayRequest.getPayDate())) {
            int overdueDays = CalculatorUtils.overDueDays(billBase.getRepaymentDate(), billPayRequest.getPayDate());
            overdueServiceFee = tradeBase.getOverdueServiceFee();
            overdueFee = CalculatorUtils.calculateOverdueFee(billBase.getBorrowCapital(), tradeBase.getOverdueRatio(), overdueDays);
        }

        BigDecimal realBorrowCapital = BigDecimal.ZERO;//累计实收本金
        BigDecimal realInterestFee = BigDecimal.ZERO;//累计实收利息
        BigDecimal realOverdueServiceFee = BigDecimal.ZERO;//累计实收逾期罚息服务费
        BigDecimal realOverdueFee = BigDecimal.ZERO;//累计实收逾期罚息
        if (CollectionUtils.isNotEmpty(billAccountingList)) {
            for (BillAccounting billAccounting : billAccountingList) {
                realBorrowCapital = realBorrowCapital.add(billAccounting.getRealBorrowCapital());
                realInterestFee = realInterestFee.add(billAccounting.getRealInterestFee());
                realOverdueServiceFee = realOverdueServiceFee.add(billAccounting.getRealOverdueServiceFee());
                realOverdueFee = realOverdueFee.add(billAccounting.getRealOverdueFee());
            }
        }

        //计算金额空缺
        BigDecimal realBorrowCapitalShortfall = borrowCapital.subtract(realBorrowCapital);
        BigDecimal realInterestFeeShortfall = interestFee.subtract(realInterestFee);
        BigDecimal realOverdueServiceFeeShortfall = overdueServiceFee.subtract(realOverdueServiceFee);
        BigDecimal realOverdueFeeShortfall = overdueFee.subtract(realOverdueFee);

        BillAccounting billAccounting = new BillAccounting()
                .setRepaymentBizNo(tradeBillDetail.getBizNo())
                .setPayBizNo(billPayRequest.getPayBizNo())
                .setTradeNo(billBase.getTradeNo())
                .setBillNo(billBase.getBillNo())
                .setOriginalCapital(new BigDecimal(billPayRequest.getCapital()))
                .setAccountedTime(billPayRequest.getPayDate());
        //平本金部分
        if (capital.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal tmp = null;
            if (capital.compareTo(realBorrowCapitalShortfall) > 0) {
                tmp = realBorrowCapitalShortfall;
            } else {
                tmp = capital;
            }
            capital = capital.subtract(tmp);
            billAccounting.setRealBorrowCapital(tmp);
        }
        //平利息部分
        if (capital.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal tmp = null;
            if (capital.compareTo(realInterestFeeShortfall) > 0) {
                tmp = realInterestFeeShortfall;
            } else {
                tmp = capital;
            }
            capital = capital.subtract(tmp);
            billAccounting.setRealInterestFee(tmp);
        }
        //平逾期服务费部分
        if (capital.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal tmp = null;
            if (capital.compareTo(realOverdueServiceFeeShortfall) > 0) {
                tmp = realOverdueServiceFeeShortfall;
            } else {
                tmp = capital;
            }
            capital = capital.subtract(tmp);
            billAccounting.setRealOverdueServiceFee(tmp);
        }
        //平逾期费部分
        if (capital.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal tmp = null;
            if (capital.compareTo(realOverdueFeeShortfall) > 0) {
                tmp = realOverdueFeeShortfall;
            } else {
                tmp = capital;
            }
            capital = capital.subtract(tmp);
            billAccounting.setRealOverdueFee(tmp);
        }
        //多余部分记入其他金额
        if (capital.compareTo(BigDecimal.ZERO) > 0) {
            billAccounting.setRealOtherAmount(capital);
            capital = BigDecimal.ZERO;
        }
        //有效记账金额
        billAccounting.setPaidCapital(
                DecimalUtils.round((billAccounting.getRealBorrowCapital() == null ? BigDecimal.ZERO : billAccounting.getRealBorrowCapital()).doubleValue()
                        + (billAccounting.getRealInterestFee() == null ? BigDecimal.ZERO : billAccounting.getRealInterestFee()).doubleValue()
                        + (billAccounting.getRealOverdueServiceFee() == null ? BigDecimal.ZERO : billAccounting.getRealOverdueServiceFee()).doubleValue()
                        + (billAccounting.getRealOverdueFee() == null ? BigDecimal.ZERO : billAccounting.getRealOverdueFee()).doubleValue(), 2)
        );

        return billAccounting;
    }

    private String createBillNo(String userCode) {
        return IdUtils.genId() + "TB";
    }

    private String createBillRepaymentNo(String userCode) {
        return IdUtils.genId() + "TR";
    }

    private Date timeAppointed(Date date, int delta) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if (delta != 0) {
            cal.add(Calendar.DATE, delta);
        }
        return cal.getTime();
    }

    /**
     * 判断是否超过28号，超过28号则返回当月28号
     *
     * @param date
     * @return
     */
    public static Date getSuitableDate(Date date) {
        int day = Integer.parseInt(DateUtil.getDays(date));
        if (day < 28) {
            return date;
        }
        String dateStr = DateUtil.getDate(date, "yyyy-MM") + "-28";
        return DateUtil.getDate(dateStr, "yyyy-MM-dd");
    }
}
